﻿<template>
    <div>
        <div v-if="checkDone" v-bind="$attrs">
            <component v-if="customCtr.show && customCtr.selfUI" :key="customCtr.key" :is="customCtr.cmpName" ref="cusCmp"
                v-bind="customCtr.attrs"></component>
            <el-card v-else class="box-card">
                <template #header>
                    <div class="card-header">
                        <div class="card-title">

                            <el-space spacer="|">
                                <el-link type="primary" v-if="showBack2" @click="goback()">
                                    <el-icon>
                                        <ArrowLeftBold />
                                    </el-icon>返回</el-link>
                                <el-space v-if="Array.isArray(title2)" spacer="/" wrap>
                                    <el-link v-for="it in title2" :key="it" :type="it.type" :title="it.label" @click="clickTitle(it)">{{ trimStr(it.label,8) }}</el-link>
                                </el-space>
                                <span v-else>{{ title2 }}</span>
                            </el-space>
                        </div>
                        <my-search-bar :tbname="tbname2" v-model="search" v-if="ready && !customCtr.show && !customCtr.hideSearchBar" ref="search_bar"
                            :excludeSearch="excludeSearch">
                            <template #action>

                                <template v-if="allowShowCart"  >
                                    <el-button v-if="cart_options.length==0 || !cart_count" type="success" @click="showCart()">
                                        <el-badge :value="cart_count" class="cart_item" type="primary">
                                            <el-icon :size="24" ><ShoppingCart /></el-icon>
                                        </el-badge>
                                    </el-button>
                                    <el-dropdown v-else placement="top-start" @command="showCart" >
                                        <el-button type="success">
                                            <el-badge :value="cart_count" class="cart_item" type="primary">
                                                <el-icon :size="24" ><ShoppingCart /></el-icon>
                                            </el-badge>
                                        </el-button>
                                        <template #dropdown>
                                            <el-dropdown-menu>
                                            <el-dropdown-item v-for="op in cart_options" :command="op.value" :key="op.value">{{ op.label }}</el-dropdown-item>
                                            </el-dropdown-menu>
                                        </template>
                                    </el-dropdown>
                                </template>

                                <el-button v-if="checkAddDone" type="success" @click="onAdd()">{{ getLabel("新增") }}</el-button>
                                <el-button v-if="allowExport" type="primary" @click="onExport">导出</el-button>

                                <slot v-if="$slots.search_action" name="search_action"></slot>
                                <template v-for="btn in search_action2" :key="btn">
                                    <template v-if="(btn.label || btn.icon) && checkIf(btn.vif)">
                                        <el-dropdown v-if="getOps2(btn.options)" split-button :type="btn.type"
                                            @command="doClick(btn.click, $event)" 
                                            @click="doClick(btn.click, $event)">
                                            {{ getBtnLabel(btn.label) }}
                                            <template #dropdown>
                                                <el-dropdown-menu>
                                                    <template v-for="menu in getOps2(btn.options)" :key="menu">
                                                        <el-dropdown-item v-if="menu.label != '-'" :command="menu.value">{{ menu.label }}</el-dropdown-item>
                                                        <el-dropdown-item v-else divided></el-dropdown-item>
                                                    </template>
                                                </el-dropdown-menu>
                                            </template>
                                        </el-dropdown>
                                        <el-button v-else :type="btn.type" @click="doClick(btn.click, $event)"  :icon="getBtnIcon(btn)" :disabled="checkDisabled(btn.disabled)">{{ getBtnLabel(btn.label) }}</el-button>
                                    </template>
                                </template>

                            </template>
                        </my-search-bar>
                        <div v-if="customCtr.show && customCtr.actions">
                            <template v-for="btn in customCtr.actions" :key="btn">
                                <template v-if="(btn.label || btn.icon) && checkIf(btn.vif)">
                                    <el-dropdown v-if="btn.options" split-button :type="btn.type"
                                        @command="doClick(btn.click, $event)" 
                                        @click="doClick(btn.click, $event)">
                                        {{ getBtnLabel(btn.label) }}
                                        <template #dropdown>
                                            <el-dropdown-menu>
                                                <template v-for="menu in btn.options" :key="menu">
                                                    <el-dropdown-item v-if="menu.label != '-'" :command="menu.value">{{ menu.label }}</el-dropdown-item>
                                                    <el-dropdown-item v-else divided></el-dropdown-item>
                                                </template>
                                            </el-dropdown-menu>
                                        </template>
                                    </el-dropdown>
                                    <el-button v-else :type="btn.type" @click="doClick(btn.click, $event)"  :icon="getBtnIcon(btn)" :disabled="checkDisabled(btn.disabled)">{{ getBtnLabel(btn.label) }}</el-button>
                                </template>
                            </template>
                        </div>
                    </div>
                    <div class="card-header" v-if="headerComponent && pageData">
                        <component :is="headerComponent" ref="headerComponent"/>
                    </div>
                </template>

                <el-tabs v-if="tbsSearch" v-model="tbsSearchValue">
                    <el-tab-pane v-for="item in getTbsSearch" :label="getTbsLabel(item.label, item.value)"
                        :name="String(item.value)" :key="String(item.value)">
                        <div v-if="item.header" class="pan-header">{{ getTbsLabel(item.header, item.value) }}</div>
                    </el-tab-pane>
                </el-tabs>

                <el-tabs v-if="tabs" v-bind="tabs.props" v-model="curTabName">
                    <el-tab-pane v-for="item in tabs.items" :label="item.label" :name="item.name" :key="item.name">
                        <my-grid v-if="item.name == 'grid'" :tbname="tbname2" ref="grid" :fields="fields2"
                            v-model:search="search" @selected="onEdit" :action-width="actionWidth" :multi="isMulti"
                            @loadedData="onLoadedData" v-bind="item.props">
                            <template #action="scope" v-if="$slots.grid_action || allowDelete2 || allowShowCart">
                                <el-link type="success" v-if="allowAdd2Cart(scope.row)" @click="add2Cart(scope.row)">+购物车</el-link>

                                <slot v-if="$slots.grid_action" name="grid_action" :row="scope.row"></slot>

                                <el-link v-else-if="allowDelete2" type="primary" @click="onDelete(scope.row)">删除</el-link>

                            </template>
                        </my-grid>

                        <component v-else :key="item.name" :is="item.control" :ref="item.name" :search="search"
                            :pageData="pageData" :cfg="cfg" v-bind="item.props"></component>
                    </el-tab-pane>
                </el-tabs>

                <template v-else-if="ready && !customCtr.show">
                    <split-container v-if="cfg.tree" :initial-width="treeWidth" :style="{height:this.cfg.tree.height}">
                        <template #left>
                            <div v-if="treeActions?.length>0" style="border-bottom: 1px solid #ccc; padding-bottom: 4px;">
                                <el-space wrap spacer="|">
                                    <template v-for="btn in treeActions" :key="btn">
                                        <template v-if="btn.label && checkIf(btn.vif)">
                                            <template v-if="btn.options && getOps2(btn.options)">
                                                <el-link  v-for="menu in getOps2(btn.options)" type="menu.type" @click="doClick(btn.click, menu.value)" :disabled="checkDisabled(menu.disabled)">{{menu.label }}</el-link>
                                            </template>
                                            <el-link v-else :type="btn.type" @click="doClick(btn.click, $event)" :disabled="checkDisabled(btn.disabled)">{{btn.label }}</el-link>
                                        </template>
                                    </template>
                                </el-space>
                            </div>
                            <el-tree ref="tree" highlight-current 
                                v-bind="cfg.tree.attrs"
                                :node-key="cfg.tree.idField || 'ID'"
                                @current-change="selectTreeNode"
                                @node-click="treeNodeClick"
                                style="width: 100%; min-height:400px;"
                                :props="cfg.tree.props" :load="loadTreeNode" lazy>
                                <template #default="{ node, data }">
                                    <span class="tree-node">
                                        <component v-if="cfg.tree.nodeControl" :is="cfg.tree.nodeControl" :node="node" :data="data" />
                                        <span v-else>{{ node.label }}</span>
                                        <span v-if="$mbh5">
                                            <el-link type="primary" @click="treeNodeClick(data)">选择</el-link>
                                        </span>
                                    </span>
                                </template>
                            </el-tree>
                        </template>

                        <template #right>
                            <my-grid :tbname="tbname2" ref="grid" :fields="fields2"
                                v-model:search="search" @selected="onEdit" :action-width="actionWidth" :multi="isMulti"
                                @loadedData="onLoadedData">
                                <template #action="scope" v-if="$slots.grid_action || allowDelete2 || allowShowCart">
                                    <slot v-if="$slots.grid_action" name="grid_action" :row="scope.row"></slot>
                                    <el-link v-else-if="allowDelete2" type="primary" @click="onDelete(scope.row)">删除</el-link>
                                    <el-link v-if="allowShowCart && allowAdd2Cart(scope.row)" type="success" @click="add2Cart(scope.row)">+购物车</el-link>
                                </template>
                            </my-grid>

                        </template>
                    </split-container>
                    <my-grid v-else :tbname="tbname2" ref="grid" :fields="fields2"
                    v-model:search="search" @selected="onEdit" :action-width="actionWidth" :multi="isMulti"
                    @loadedData="onLoadedData">
                    <template #action="scope" v-if="$slots.grid_action || allowDelete2 || allowShowCart">

                        <slot v-if="$slots.grid_action" name="grid_action" :row="scope.row"></slot>

                        <el-link v-else-if="allowDelete2" type="primary" @click="onDelete(scope.row)">删除</el-link>

                        <el-link v-if="allowShowCart && allowAdd2Cart(scope.row)" type="success" @click="add2Cart(scope.row)">+购物车</el-link>
                    </template>
                    </my-grid>
                </template>

                <component v-if="customCtr.show" :key="customCtr.key" :is="customCtr.cmpName" ref="cusCmp"
                    v-bind="customCtr.attrs">
                </component>

                
            </el-card>
            

        </div>
        <el-empty v-else description="没有数据" style="width: 100%;" />

        <my-float-button v-if="tipMessage">
            <el-button type="primary">{{tipMessage}}</el-button>
        </my-float-button>

    </div>
</template>

<script>import { nextTick, reactive, resolveComponent, toRaw, watch } from "vue";
import { Server, Utils } from "./main";
export default {
    name: "MyListPage",
    props: {
        tbname: {
            type: String,
            required: true,
        },
        allowAdd: {
            type: Boolean,
            default: true,
        },
        allowSearch: {
            type: Boolean,
            default: true,
        },
        allowEdit: {
            type: Boolean,
            default: true,
        },
        allowDelete: {
            type: Boolean,
            default: true,
        },
        allowExport: {
            type: Boolean,
            default: false
        },
        showBack: {
            type: Boolean,
            default: false
        },
        //默认的搜索条件
        defaultSearch: Object,
        //子记录编辑界面
        formName: {
            type: String,
            default: "my-form-dlg",
        },
        fields: String,
        //页面标题
        label: String,
        actionWidth: {
            type: [String, Number],
            default: "120",
        },
        pformSearch: Object, //父亲form传来的过滤条件
        formData: Object,
        columns: Array,//用于替换dm上定义的列
        excludeSearch: String,//额外排除的搜索字段分号(;)分隔
        title: String,
    },
    provide() {
        return {
            MyListPage: this,
        };
    },
    data() {
        return {
            checkAddDone: false,//检查是否允许添加的结果
            checkDone: false,//检查通过以后才能显示
            ready: false,
            tbsSearch: null,
            tbsSearchValue: "",
            search: {},
            cfg: {},
            tbname2: "",//tabs多标签的时候，不同标签可能对应不同的表
            search_action: null,
            search_action2: null,
            //自定义组件
            customCtr: {
                //是否完全使用自己UI
                selfUI: false,
                //是否隐藏搜索栏
                hideSearchBar:false,
                //自定义组件名字
                cmpName: "",
                //自定义组件属性
                attrs: {},
                fields: "",
                search: {},
                actions: null,
            },
            curTabName: "grid",
            pageData: null,
            showBack2: false,
            title2: "",
            cart:[],
            headerComponent:"",//自定义标题栏组件名称
            treeIsCollapse:false,
            treeNode:null,
            tipMessage:null
        };
    },
    created() {
        this.initByTbname(this.tbname);
        this.showBack2 = this.showBack;
        this.title2 = this.title;
    },
    methods: {
        trimStr(str, maxlen){
            if (maxlen < 5) {
               maxlen = 5;
            }
            if(str && str.length>maxlen){
                return str.substring(0, maxlen-3)+"...";
            }
            return str;
        },
        clickTitle(lnk){
            if(typeof(lnk.click)=="function"){
                lnk.click(lnk);
            }else{
                location = lnk.url;
            }
        },
        getLabel(label) {
            let labels = this.cfg.grid.labels || this.cfg.labels;
            if (labels) {
                return labels[label] || label;
            }
            return label;
        },
        call(action, pm) {
            if (action.indexOf("/") != -1) {
                return $server.call(action, pm, this.$el);
            }
            return $server.doAction(this.tbname, pm, this.$el);
        },
        goback() {
            // if (document.referrer && document.referrer != location.href) {
            //     location.assign(document.referrer);
            // } else {
            //     history.back();
            // }
            history.back();
        },
        getSelected(){
            if(this.$refs.grid){
                return this.$refs.grid.getSelected();
            }
            return [];
        },

        //打开自定义组件显示
        showCustomCtr(cuscfg) {
            let actions = cuscfg.actions;
            if(actions){
                actions.forEach(async ac=>{
                    if(ac.options && typeof(ac.options)=="string"){
                        ac.options = await Utils.parseOptions(ac.options)
                    }
                })
            }

            this.customCtr = {
                selfUI: cuscfg.selfUI,
                hideSearchBar: cuscfg.hideSearchBar,
                cmpName: cuscfg.cmpName,
                attrs: cuscfg.attrs,
                key: cuscfg.key || this.tbsSearchValue || cuscfg.cmpName,
                fields: cuscfg.fields,
                formName: cuscfg.formName,
                show: cuscfg.cmpName && cuscfg.cmpName.length > 0,
                actions: actions
            }
            if (cuscfg.search) {
                this.search = { ...this.search, ...cuscfg.search }
            }
        },
        //关闭自定义组件显示
        hideCustomCtr() {
            this.customCtr.show = false;
        },
        initByTbname(tb) {
            if (!tb) return;
            this.$server.getDM(tb).then((res) => {
                this.tbname2 = tb;
                this.search = {};
                this.cfg = res;
                this.tb = res;//和MyFormDlg一样，提供一个tb属性

                //把自定义组件注册到页面
                if (res._Components) {
                    if (!this.$options.components) {
                        this.$options.components = {};
                    }
                    Object.keys(res._Components).forEach(key => {
                        let cmp = toRaw(res._Components[key]);
                        this.$options.components[key] = cmp;
                    });
                }

                if (this.columns && Array.isArray(this.columns) && this.columns.length > 0) {
                    this.cfg.columns = [...this.cfg.columns, ...this.columns];
                }
                this.cfg.title = this.label || this.cfg.title;
                this.title2 = this.title || this.cfg.title;
                this.search = { ...this.search, ...this.defaultSearch };

                let sorder = localStorage.getItem(`${this.tbname}_sort`);
                if(sorder){
                    this.search = {...this.search, order: sorder};
                }

                //直接定义一个custList，取代默认的list显示方式
                let custList = this.cfg.custList_pc || this.cfg.custList;
                if (custList && typeof (custList) == "object") {
                    this.showCustomCtr(custList);
                }

                this.search_action = res.search_action;
                this.search_action2 = this.search_action;

                this.tbsSearch = this.cfg.tbsSearch_pc || this.cfg.tbsSearch;
                if (this.tbsSearch) {
                    let fd = this.tbsField;

                    if (fd.control == "select" && fd.select && fd.select.cfg && fd.select.cfg.tbname) {
                        //取动态options
                        let moduleName = fd.select.cfg.tbname;
                        let valueField = fd.select.cfg.valueField || "ID";
                        let labelField = fd.select.cfg.labelField || "Name";
                        this.$server.list(moduleName, 1, 1000, fd.select.cfg.search).then((res) => {
                            var lst = [];
                            var records = res.records || res;
                            for (var ix = 0; ix < records.length; ix++) {
                                var record = records[ix];
                                var item = {
                                    value: record[valueField],
                                    label: record[labelField],
                                };
                                lst.push(item);
                            }
                            fd.options = lst;
                            this.tbsSearchValue = fd.default ? String(fd.default) : String(lst[0].value);
                        });
                    } else {
                        this.tbsSearchValue = fd ? String(fd.default || "") : "";
                    }
                    if (!this.tbsSearchValue) {
                        const opts = this.getTbsSearch;
                        if (opts && opts.length > 0) {
                            this.tbsSearchValue = String(opts[0].value);
                        }
                    }

                }

                if(this.cfg.tree){
                    //数形结构默认属性
                    
                    this.cfg.tree.props = {
                        label: this.cfg.tree.labelField || "label",
                        children:this.cfg.tree.childrenField || "children",
                        isLeaf: 'leaf',
                    };
                }
                this.proceParenFormSearch();

                this.headerComponent = this.cfg?.grid?.headerComponent;
                //处理扩展方法
                if (this.cfg.ext) {
                    if (this.cfg.ext.common) {
                        this.proceGridMethods(this.cfg.ext.common.methods, this);
                    }
                    if (this.cfg.ext.grid) {
                        this.proceGridMethods(this.cfg.ext.grid.methods, this);
                    }
                }
                if(this.cfg.grid && this.cfg.grid.methods){
                    this.proceGridMethods(this.cfg.grid.methods, this);
                }
                if(this.onCreated){
                    this.onCreated.apply(this);
                }
            }).then(res => {
                //如果 allow段有定义showList，则等待showList执行结果，==true时才显示主界面，否则显示空白
                if (this.cfg.allow && this.cfg.allow.showList) {
                    if (typeof (this.cfg.allow.showList) == "function") {
                        var p = this.cfg.allow.showList.apply(this);
                        Promise.resolve(p).then(res1 => {
                            this.checkDone = res1 == true;
                        });
                    } else {
                        this.checkDone = this.cfg.allow.showList == true;
                    }
                } else {
                    this.checkDone = true;
                }
                this.proceAddRule();
            });
        },
        proceParenFormSearch() {
            let nn = { ...this.search };
            if (this.pformSearch && this.formData) {
                Object.keys(this.pformSearch).map((p) => {
                    let v = this.pformSearch[p];
                    if (v) {
                        nn[p] = this.formData[v];
                    }
                });
            }
            this.search = nn;
            this.ready = true;
        },
        loadData(pageIndex,row) {
            if (this.$refs.grid) {
                this.$refs.grid.clearSelection();
                this.$refs.grid.loadData(pageIndex, null, row);
            }
        },
        onEdit(row) {
            if(this.tbname==this.tbname2){
                const onRowClick = this.cfg?.grid?.methods?.onRowClick 
                    || this.cfg.ext?.grid?.methods?.onRowClick  
                    || this.cfg.ext?.common?.methods?.onRowClick;

                if (onRowClick) {
                    onRowClick.apply(this, [row]);
                    return;
                }
            }

            //没有定义get方法，就不
            let callCfg = this.cfg.call_pc || this.cfg.call;
            if (callCfg && !callCfg.get) return;

            if (this.allowEdit2) {
                Server.dlg.edit(this.tbname2, this.getPrimaryKey(row)).then(() => {
                    this.loadData(undefined, row);
                });
            } else {
                this.onView(row);
            }
        },
        getPrimaryKey(row) {
            return row.ID || row.id || 0;
        },
        onView(row) {
            Server.dlg.view(this.tbname2, this.getPrimaryKey(row));
        },
        onAdd(item) {
            if (!item) {
                item = { ID: 0 };
                Object.keys(this.search).forEach(key => {
                    if (["pageIndex", "pageSize"].indexOf(key) == -1) {
                        let val = this.search[key];
                        if (val) {
                            item[key] = val;
                        }
                    }
                })
            }
            if (this.checkAddDone == false) return;
            if (this.$refs.grid && this.$refs.grid.tree) {
                var prows = this.$refs.grid.$refs.grid.getSelectionRows();
                if (prows && prows.length > 0) {
                    item = { ...item };
                    let row = prows[0];
                    let idfield = this.$refs.grid.tree.id || (("id" in row) ? "id" : "ID");
                    let pid = this.$refs.grid.tree.pid || (("pid" in row) ? "pid" : "PID");
                    item[pid] = row[idfield];
                }
            }
            let props = { defaultCreate: this.defaultSearch };
            let pp = this.checkAddDone;
            if (pp && pp.formName) {
                props.formName = pp.formName;
            }
            Server.dlg.create(this.tbname2, 0, item, props).then(res => {
                this.loadData();
            });
        },
        onDelete(row) {
            if (!this.allowEdit2) return;

            let delFun = null;
            if (this.cfg.form && this.cfg.form.vue && this.cfg.form.vue.computed && this.cfg.form.vue.computed.allowDelete) {
                delFun = this.cfg.form.vue.computed.allowDelete;
            } else if (this.cfg.ext && this.cfg.ext.form && this.cfg.ext.form.computed && this.cfg.ext.form.computed.allowDelete) {
                delFun = this.cfg.ext.form.computed.allowDelete;
            } else if (this.cfg.ext && this.cfg.ext.common && this.cfg.ext.common.computed && this.cfg.ext.common.computed.allowDelete) {
                delFun = this.cfg.ext.common.computed.allowDelete;
            } else if (this.cfg.ext && this.cfg.ext.common && this.cfg.ext.common.methods && this.cfg.ext.common.methods.allowDelete) {
                delFun = this.cfg.ext.common.methods.allowDelete;
            }
            if (delFun) {
                let obj = { tb: this.cfg, cfg: this.cfg, formData: row };
                if (!delFun.apply(obj, [row])) {
                    $msg.error("不允许删除");
                    return;
                }
            }

            this.$server.delete(this.tbname, this.getPrimaryKey(row)).then(() => {
                this.loadData();
            });
        },
        onSetStatus(row, status) {
            this.$server.setStatus(this.tbname, this.getPrimaryKey(row), status).then(() => {
                this.loadData();
            });
        },
        onExport() {
            if (this.doExport && typeof this.doExport == "function") {
                this.doExport.apply(this);
                return;
            }
            let listAction = "";
            if (this.cfg.call && this.cfg.call.list) {
                listAction = this.cfg.call.list;
            }
            var search = this.$refs.grid.newSearch;
            var search2 = {};
            for (let key in search) {
                let v = search[key];
                if (v || typeof (v) == "number" || v != "") {
                    search2[key] = v;
                }
            }
            var pms = {
                tbname: this.tbname,
                list: listAction,
                fields: this.$refs.grid.cols,
                search: search2,
                title: this.cfg.title
            };

            this.$server.download("/Common/Export", pms);
        },

        checkIf(vif, args) {
            if (typeof vif == "function") {
                return vif.apply(this, args);
            }
            return true;
        },
        checkDisabled(vif) {
            if (typeof vif == "function") {
                return vif.apply(this);
            }
            return false;
        },
        proceGridMethods(m, me) {
            for (let key in m) {
                let func = m[key];
                if (typeof func == "function") {
                    me[key] = func.bind(me);
                    //console.log("bind"+key, me);
                } else {
                    console.log("method定义错误：" + key);
                }
            }
        },
        doClick(func, event, ix) {
            let me = this;
            Utils.doFunc(func, me, event != undefined ? [event] : undefined);
        },
        getTbsLabel(lb, val) {
            if (lb && lb.indexOf("${") != -1) {
                return lb.replace(/\$\{([^}]+)\}/g, (t1, field) => {
                    val = String(val);
                    if (val == this.tbsSearchValue) {
                        switch (field) {
                            case "total":
                                let total = Utils.getValByKey(this.$refs, "grid.page.totalCount") || 0;
                                return total;
                            default:
                                return field;
                        }
                    } else {
                        return "";
                    }
                });
            }
            return lb;
        },
        onLoadedData(pageData) {
            this.pageData = pageData;
            if(this.onLoadData){
                this.onLoadData.apply(this);
            }

        },
        setTitle(title){
            this.title2 = title;
        },
        setShowBack(flag){
            this.showBack2 = flag==true;
        },
        proceAddRule() {
            if (this.cfg.allow && this.cfg.allow.add != undefined) {
                if (typeof (this.cfg.allow.add) == "function") {
                    let q = this.cfg.allow.add.apply(this);
                    Promise.resolve(q).then(r=>{
                        this.checkAddDone = r;
                    });
                    return;
                }
                this.checkAddDone = this.cfg.allow.add;
                
                return;
            }
            this.checkAddDone = this.allowAdd;
        },
        add2Cart(row){
            if(!row)return;

            let item = this.cart.find(x=>x.ID==row.ID);
            if(!item){
                item = {...row, _cart_count:1};
                this.cart.push(item);
            }else{
                item._cart_count++;
            }
        },
        showCart(cmd){
            if(this.cart_count>0){
                let items = this.cart.map(x=>({...x}));
                this.cfg.ShoppingCart.onShowCart.apply(this, [cmd, items]);
            }
        },
        allowAdd2Cart(row){
            if(this.allowShowCart && row && this.cfg.ShoppingCart.allowAdd2Cart){
                return this.checkIf(this.cfg.ShoppingCart.allowAdd2Cart, [row]);
            }
            return false;

        },
        //tree
        selectTreeNode(data,node){
            this.treeNode = data;
        },
        loadTreeNode(node, resolve, reject) {
            let keyField = this.cfg.tree.idField || "ID";
            let pid = node?.data[keyField];
            if(typeof(this.cfg.tree.loadNode)=="function"){
                this.cfg.tree.loadNode.apply(this,[node, (items)=>{
                    resolve(items);
                    if(!pid){
                        nextTick(()=>{
                            //加载跟节点的时候，自动选中根节点
                            let cKey = items[0][keyField];
                            this.$refs.tree.setCurrentKey(cKey);
                            this.treeNodeClick(items[0]);
                        })
                    }
                }, reject]);
                return;
            }
            if(!pid){
                pid = this.search[this.cfg.tree.searchField];
                if(!!pid){
                    $server.get(this.cfg.tree.tbname, pid).then(res=>{
                        resolve([res]);
                        nextTick(()=>{
                            //加载跟节点的时候，自动选中根节点
                            let cKey = res[keyField];
                            this.$refs.tree.setCurrentKey(cKey)
                            this.treeNodeClick(res);
                        })
                    })
                    return;
                }
            }
            let pidField = this.cfg.tree.parentField || "pid";
            let pm = {};
            pm[pidField] = pid;
            
            $server.list(this.cfg.tree.tbname, 1,10000, pm).then(res => {
                let items = res.records || res;
                resolve(items);
            });
        },
        treeNodeClick(data,node,el,e){
            const key = data[this.cfg.tree.idField || "ID"];
            if(this.search[this.cfg.tree.searchField] == key)return;

            if(typeof(this.cfg.tree.onSelectNode)=="function"){
                this.cfg.tree.onSelectNode.apply(this, arguments);
                return;
            }
            if(this.cfg.tree.searchField){
                this.search[this.cfg.tree.searchField] = key;
                nextTick(()=>{
                    this.loadData(1);
                })
            }
        },
        addTreeNode(node, pnode){
            if(this.$refs.tree){
                pnode = pnode || this.$refs.tree?.getCurrentNode();
                this.$refs.tree.append(node, pnode);
            }
        },
        removeTreeNode(node){
            if(this.$refs.tree){
                this.$refs.tree.remove(node);
            }
        },
        updateTreeNode(node){
            if(this.$refs.tree){
                let key = node[this.cfg.tree.idField || "ID"];
                let snode = this.$refs.tree.getNode(key);
                snode.data = node;
                this.$refs.tree.updateKeyChildren(key,node);
            }
        },
        getCurrentTreeNode(){
            if(this.$refs.tree){
                return this.$refs.tree.getCurrentNode();
            }
            return null;
        },
        getOps2(ops){
            if(!ops) return null;
            if(typeof ops=="string"){
              ops = ops.replaceAll("::", "◲◲◲◲");
                let hasIndex = ops.indexOf(":") > 0;
                let index = 0;
                ops = ops.split(/[，；,;]/g).map(p => {
                    if (hasIndex) {
                        let ix = p.indexOf(":");
                        if (ix > 0) {
                            let index0 = parseInt(p.substring(0, ix));
                            if (!Number.isNaN(index0)) {
                                index = index0;
                                return { value: index++, label: p.substring(ix + 1) };
                            } else {
                                index++;
                                return { value: p.substring(0, ix), label: p.substring(ix + 1) };
                            }
                        }
                        return { value: index++, label: p.substring(ix + 1) };
                    }
                    return { value: p, label: p };
                }).map(x => {
                    if (typeof (x.value) == "string") {
                        x.value = x.value.replaceAll("◲◲◲◲", ":");
                    }
                    x.label = x.label.replaceAll("◲◲◲◲", ":");
                    return x;
                });
            }
            const ops2 = (ops || []).filter(x=>this.checkIf(x.vif));
            if(ops2.length>0){
                return ops2;
            }
            return null;
        },
        setCurrentTreeKey(key){
            this.$refs.tree.setCurrentKey(key);
        },
        getBtnLabel(lb){
            if(typeof lb == "function"){
                return lb.apply(this);
            }
            return lb;
        },
        getBtnIcon(btn){
            if(typeof btn.icon=="function"){
                return btn.icon.apply(this);
            }
            return btn.icon;
        }
    },
    computed: {
        allowShowCart(row){
            return this.cfg.ShoppingCart && this.cfg.ShoppingCart.onShowCart;
        },
        cart_count(){
            let count = this.cart.reduce((p,c)=>p + (c._cart_count || 0),0);
            return count>0? count:"";
        },
        cart_options(){
            let ops = (this.cfg.ShoppingCart.options || "").split(";").filter(x=>!!x && x.length>0).map(x=>({label:x,value:x}));
            return ops;
        },
        selectedRows(){
            var prows = this.$refs.grid.$refs.grid.getSelectionRows();
            return prows;
        },
        isMulti(){
            if(this.cfg.grid.multi){
                if(typeof(this.cfg.grid.multi)=="function"){
                    return this.cfg.grid.multi.apply(this);
                }
                return this.cfg.grid.multi;
            }
            return false;
        },
        getTbsSearch() {
            if (this.tbsSearch) {
                let fd = this.tbsField;

                if (fd && fd.options) {
                    return fd.options;
                }
            }
            return [];
        },
        allowSearch2() {
            if (!this.allowSearch) return false;

            var s1 = this.cfg.search || "";
            if (typeof s1 == "string") return s1.trim().length > 0;
            return s1.length > 0;
        },
        
        allowEdit2() {
            if (this.cfg.allow && this.cfg.allow.edit != undefined) {
                if (typeof (this.cfg.allow.edit) == "function") {
                    return this.cfg.allow.edit.apply(this);
                }
                return this.cfg.allow.edit;
            }

            return this.allowEdit;
        },
        formName2() {
            return this.customCtr.formName || this.formName;
        },
        fields2() {
            return this.customCtr.fields || this.fields;
        },
        allowDelete2() {
            if (this.cfg.allow && this.cfg.allow.delete != undefined) {
                if (typeof (this.cfg.allow.delete) == "function") {
                    return this.cfg.allow.delete.apply(this);
                }
                return this.cfg.allow.delete;
            }
            return this.allowDelete;
        },
        tbsField() {
            let field = this.tbsSearch;
            if (field && typeof (this.tbsSearch) == "string") {
                field = this.cfg.columns.filter((p) => p.code == this.tbsSearch)[0];
            }
            return field;
        },
        tbsFieldSearchCode() {
            let field = this.tbsField;
            if (field) {
                return field.searchCode || field.code;
            }
            return null;
        },
        tabs() {
            if (this.cfg.grid && this.cfg.grid.tabs && Array.isArray(this.cfg.grid.tabs.items) && this.cfg.grid.tabs.items.length > 0) {
                return this.cfg.grid.tabs;
            }
            return null;
        },
        //左边树结构
        treeWidth(){
            return this.cfg.tree?.width || 240;
        },
        treeActions(){
            if(Array.isArray(this.cfg.tree.actions)){
                return this.cfg.tree.actions.filter(x=>this.checkIf(x.vif));
            }
            return null;
        }
    },
    watch: {
        showBack(){
            this.showBack2 = this.showBack;
        },
        title(){
            this.title2 = this.title || this.cfg.title;
        },
        formData() {
            this.proceParenFormSearch();
        },
        tbsSearchValue(val) {
            let spm = {};

            if (this.tbsFieldSearchCode) {
                spm[this.tbsFieldSearchCode] = parseInt(val) || val;
            }

            this.search = { ...this.search, ...spm };

            const opts = this.getTbsSearch;
            let item = opts.filter(p => p.value == val)[0];
            if (item && item.tbname) {
                this.tbname2 = item.tbname;
                this.search_action2 = item.search_action;
            } else {
                this.tbname2 = this.tbname;
                this.search_action2 = this.search_action;
            }

            let cusobj = item;
            if (item.cus) {
                switch (typeof (item.cus)) {
                    case "function":
                        cusobj = item.cus.apply(this);
                        break;
                    case "object":
                        cusobj = { ...item.cus };
                        break;
                    case "string":
                        cusobj = { cmpName: resolveComponent(item.cus) };
                        break;
                }
            }
            if (cusobj && typeof (cusobj) == "object") {
                this.showCustomCtr(cusobj);
            } else {
                this.hideCustomCtr();
            }
        },
        tbname(val) {
            this.initByTbname(val);
        },
        defaultSearch() {
            //routerview keepalive 的时候， 只有defaultSearch条件变化， 此时要重新加载数据
            this.search = { ...this.search, ...this.defaultSearch };
            //search条件传递到grid
            nextTick(()=>{
                this.loadData(1);
            });
        },
        curTabName(newVal) {
            let cmp = this.$refs[newVal];
            if (cmp && cmp[0].onShow) {
                this.$nextTick(() => {
                    cmp[0].onShow();
                })
            }
        },
        'cfg.allow.add':function(newVal){
            if(typeof(newVal)=="function"){
                var p = newVal.apply(this);
                Promise.resolve(p).then(res=>{
                    this.checkAddDone=res==true;
                });
            }else{
                this.checkAddDone = newVal==true;
            }
        },
        'cfg.allow.showList':function(newVal){
            if(typeof(newVal)=="function"){
                var p = newVal.apply(this);
                Promise.resolve(p).then(res1 => {
                    this.checkDone = res1 == true;
                });
            }else{
                this.checkDone = newVal==true;
            }
        }
    }
};
</script>

<style scoped>
.card-header{
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.pan-header {
    font-size: larger;
    font-weight: bolder;
    border-bottom: 1px solid #ccc;
    padding: 10px;
    margin: 10px;
}
.tree-node{
    display: flex;
    flex: 1; 
    align-items: center;
    justify-content: space-between;
    padding-right: 8px; 
    border-bottom: 1px dashed #ccc;
}
</style>